﻿using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Red.Controls.Shell;
using Red.Controls.Tile;
using Red.Controls.Utils;
using Red.Core;

namespace Red.Launcher.ViewModels.ExternalApplications
{
    public class Manager : IStringProvider<Tile.Strings>
    {
        #region Public Properties

        public ObservableCollection<Application> Applications { get; private set; } = new ObservableCollection<Application>();

        public bool Loaded { get; private set; }

        #endregion Public Properties

        #region C-Tor

        public Manager()
        {
        }

        #endregion  C-Tor

        #region Public Methods

        public void FindAndRunApplicationWithPath( string path )
        {
            Application application = Applications.FirstOrDefault( app => app.Path == path );
            Run( application );
        }

        public void Run( Application application )
        {
            if ( application != null )
            {
                ProcessStartInfo info = new ProcessStartInfo( application.ResolvedPath );

                info.Arguments = application.ResolvedCommandLineParameters;

                // UseShellExecute must be false if we have any environmental variables to set
                // This means that environmental variables are only applicable to executables
                // (So the user can't select a .txt file and specify a variable and expect it to work)
                info.UseShellExecute = application.EnvironmentVariables.Count == 0;

                foreach ( Application.EnvironmentVariable ev in application.EnvironmentVariables )
                {
                    if ( !string.IsNullOrEmpty( ev.Key ) && !string.IsNullOrEmpty( ev.Value ) )
                    {
                        info.EnvironmentVariables.Add( ev.Key, ev.Value );
                    }
                }

                try
                {
                    Process.Start( info );
                }
                catch ( Exception e )
                {
                    Debug.Print( e.ToString() );

                    string message = $"Unable to start external application: {application.ResolvedPath}\n\n{e.Message}";
                    RedMessageBox.Show( null, RedDialogType.Ok, message, "Error", RedMessageBox.Mode.Warning );
                }
            }
        }

        public void Save()
        {
            XElement root = new XElement( "Applications" );

            foreach ( Application application in Applications )
            {
                XElement child = application.ToXml();

                root.Add( child );
            }

            string directory = Settings.ApplicationDataDirectoryPath;
            string path = Path.Combine( directory, "external.applications.xml" );
            XmlHelper.Save( root, path );
        }

        public void Load()
        {
            if( Loaded )
            {
                return;
            }

            string directory = Settings.ApplicationDataDirectoryPath;
            string path = Path.Combine( directory, "external.applications.xml" );

            if ( File.Exists( path ) )
            {
                XDocument doc = XDocument.Load( path );

                XElement applicationsElement = doc.Element( "Applications" );

                foreach ( XElement child in applicationsElement.Elements() )
                {
                    Application application = new Application();
                    application.FromXml( child );

                    Applications.Add( application );
                }
            }

            Loaded = true;
        }

        #endregion Public Methods
        
        #region IStringProvider<Tile.IStrings>

        public Tile.Strings GetValue( string key )
        {
            const string prefix = "application.";

            if ( key.StartsWith( prefix ) )
            {
                string path = key.Substring( prefix.Length );
                foreach ( Application application in Applications )
                {
                    if ( application.Path == path )
                    {
                        return new Tile.Strings()
                        {
                            Key = key,
                            Header = application.Name,
                            Description = application.Path
                        };
                    }
                }
            }

            return null;
        }

        #endregion IStringProvider<Tile.IStrings>
    }
}
